package com.springcloudlearn.controller;

import com.netflix.hystrix.*;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import com.springcloudlearn.hystrix.MyHystrixCommand;
import com.springcloudlearn.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @Package: com.springcloudlearn.controller
 * @ClassName: :ProviderController
 * @Description: spirngcloud-learn
 * @Author: Coding-Farmer
 * @Date: 2019/8/1 14:04
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;


    @RequestMapping("/hello")
    public String hello() {
        //逻辑判断省略

        //调用spring cloud服务提供者提供的服务                                        调用远程服务的返回值类型
        //return   restTemplate.getForEntity("http://localhost:8080/provider/hello",String.class).getBody();

        //将ip:port改为服务名称调用就行了
        // return   restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/hello",String.class).getBody();

        ResponseEntity<String> forEntity = restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/hello", String.class);
        String body = forEntity.getBody();
        HttpStatus statusCode = forEntity.getStatusCode();
        int statusCodeValue = forEntity.getStatusCodeValue();
        HttpHeaders headers = forEntity.getHeaders();

        System.out.println(body);
        System.out.println(statusCode);
        System.out.println(statusCodeValue);
        System.out.println(headers);
        return body;
    }


    /**
     * 调用get请求,返回一个User对象
     *
     * @return
     */
    @RequestMapping("/user")
    public User user() {
        //逻辑判断省略
        ResponseEntity<User> forEntity = restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/user", User.class);
        System.out.println(forEntity.getBody().getId() + "" + forEntity.getBody().getName() + "" + forEntity.getBody().getPhone());
        return forEntity.getBody();
    }

    /**
     * 给服务传参数Get请求
     *
     * @return
     */
    @RequestMapping("/getUser")
    public User getUser() {
        //逻辑判断省略
        String[] arr = {"2", "xxx", "4545645456"};
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1);
        map.put("name", "wwwwww");
        map.put("phone", "1213213213123");

        //ResponseEntity<User> forEntity = restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/getUser?id={0}&name={1}&phone={2}", User.class,arr);
        //ResponseEntity<User> forEntity = restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/getUser?id={id}&name={name}&phone={phone}", User.class,map);
        /*
         * restTemplate.getForObject在getForObject在getForEntity在次封装，直接获取返回值类型，相当于ResponseEntity中的getBody
         */
        User user1 = restTemplate.getForObject("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/getUser?id={id}&name={name}&phone={phone}", User.class, map);

        //System.out.println(forEntity.getBody().getId()+""+forEntity.getBody().getName()+""+forEntity.getBody().getPhone());
        System.out.println(user1.getId() + "" + user1.getName() + "" + user1.getPhone());
        return user1;
    }


    /**
     * 调用POST请求
     *
     * @return
     */
    @RequestMapping("/addUser")
    public User addUser() {
        //逻辑判断省略
        String[] arr = {"2", "xxx", "4545645456"};
        //不能使用map传递参数
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1);
        map.put("name", "wwwwww");
        map.put("phone", "1213213213123");

        /**
         *要传的表单信息，参数数据（很坑人）
         */
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("id", 1);
        multiValueMap.add("name", "xxxxx");
        multiValueMap.add("phone", "000000000");

        //使用jdk中的map传参数，接收不到
        ResponseEntity<User> userResponseEntity = restTemplate.postForEntity(
                "http://SPRINGCLOUD-SERVICE-PROVIDER/provider/addUser", multiValueMap, User.class);

        System.out.println(userResponseEntity.getBody().getId() + "" + userResponseEntity.getBody().getName() + "" + userResponseEntity.getBody().getPhone());
        return userResponseEntity.getBody();
    }


    /**
     * 调用PUT请求
     *
     * @return
     */
    @RequestMapping("/updateUser")
    public String updateUser() {
        //逻辑判断省略
        String[] arr = {"2", "xxx", "4545645456"};
        //不能使用map传递参数
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1);
        map.put("name", "wwwwww");
        map.put("phone", "1213213213123");

        /**
         *要传的表单信息，参数数据（很坑人）
         */
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("id", 1);
        multiValueMap.add("name", "xxxxx");
        multiValueMap.add("phone", "000000000");

        //使用jdk中的map传参数，接收不到
        restTemplate.put("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/updateUser", multiValueMap);

        return "SUCCESS";
    }

    /**
     * 调用DELETE请求
     *
     * @return
     */
    @RequestMapping("/deleteUser")
    public String deleteUser() {
        //逻辑判断省略
        String[] arr = {"2", "xxx", "4545645456"};
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1);
        map.put("name", "wwwwww");
        map.put("phone", "1213213213123");

        /**
         *要传的表单信息，参数数据（很坑人），只有post,PUT请求采用这种map传参数
         */
     /*   MultiValueMap<String,Object> multiValueMap=new LinkedMultiValueMap<>();
        multiValueMap.add("id",1);
        multiValueMap.add("name","xxxxx");
        multiValueMap.add("phone","000000000"); */

        //使用jdk中的map传参数，接收不到,不能使用MultiValueMap，接收不到参数
        restTemplate.delete("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/deleteUser?id={id}&name={name}&phone={phone}", map);

        return "SUCCESS";
    }

    /*
        hystrix超时时间是3.5s
    */
    @RequestMapping("/hystrix")
    //hystrix默认超时时间是1000ms，如果你后端的响应超过此时间，就会触发断路器
    //ignoreExceptions忽略指定异常，直接抛给用户，超时会调用熔断方法，不会抛出异常，因为服务端没有给调用端抛异常，只是服务降级了，被error方法拦截了
    @HystrixCommand(fallbackMethod = "error", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3500")})
    //fallbackMethod如果发生熔断，调用哪个方法
    public String hystrix() {
        //int a=10/0;//除数不能为0,这一行代码就会触发熔断

        return restTemplate.getForEntity("http://SPRINGCLOUD-SERVICE-PROVIDER/provider/hello", String.class).getBody();
    }

    public String error(Throwable throwable) {
        System.out.println("异常信息：" + throwable.getMessage());
        //访问远程服务失败，该如何处理，这些处理逻辑就可以写在该方法中
        return "ERROR";
    }

    /**
     * 自定义Hystrix请求的服务异常熔断处理
     *
     * @return
     */
    @RequestMapping("/hystrix2")
    public String hystrix2() throws ExecutionException, InterruptedException {
        //int a=10/0;//除数不能为0,这一行代码就会触发熔断
        MyHystrixCommand myHystrixCommand =
                new MyHystrixCommand(
                        com.netflix.hystrix.HystrixCommand.Setter
                                                // 分组名称用于统计
                                                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("userGroup"))
                                                // 用于监控、熔断、度量发布、缓存的Key值
                                                .andCommandKey(HystrixCommandKey.Factory.asKey("userCommandKey"))
                                                // 线程池命名，默认是HystrixCommandGroupKey的名称。
                                                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("userThreadPool"))
                                                // command 熔断相关参数配置
                                                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                                        // 隔离方式：线程池和信号量。默认使用线程池
                                                        // .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                                                        // 超时时间500毫秒
                                                        // .withExecutionTimeoutInMilliseconds(500)
                                                        // 信号量隔离的模式下，最大的请求数。和线程池大小的意义一样
                                                        // .withExecutionIsolationSemaphoreMaxConcurrentRequests(2)
                                                        // 熔断时间（熔断开启后，各5秒后进入半开启状态，试探是否恢复正常）
                                                        // .withCircuitBreakerSleepWindowInMilliseconds(5000)
                                                )
                                                // 设置线程池参数
                                                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                                                        // 线程池大小
                                                        .withCoreSize(1)),

                        restTemplate);
        //通过调用，等待返回结果，再返回前台页面，代码在往下继续执行
        //String str = myHystrixCommand.execute();
        //异步调用，该方法执行后不会马上有远程返回的结果，将来有返回结果
        Future<String> queue = myHystrixCommand.queue();

        //******写一些业务逻辑******

        //get()方法阻塞是方法，直到拿到结果，会提高系统性能
        String str = queue.get();
        return str;
    }


}
